package com.wh.wisdomsite.xbox.core.impl;

import com.wh.wisdomsite.xbox.common.XboxContext;
import com.wh.wisdomsite.xbox.core.ServiceDispatcher;
import com.wh.wisdomsite.xbox.core.ServiceMethodAdapter;
import com.wh.wisdomsite.xbox.core.event.*;
import com.wh.wisdomsite.xbox.core.threadpool.Job;
import com.wh.wisdomsite.xbox.core.XboxException;
import com.wh.wisdomsite.xbox.core.builder.RequestContextBuilder;
import com.wh.wisdomsite.xbox.core.builder.impl.DefaultRequestContextBuilder;

import com.wh.wisdomsite.xbox.common.annotation.MessageFormat;
import com.wh.wisdomsite.xbox.common.error.MainErrors;
import com.wh.wisdomsite.xbox.common.reponse.RejectedServiceErrorResponse;
import com.wh.wisdomsite.xbox.common.session.SessionManager;
import com.wh.wisdomsite.xbox.utils.json.JosnConverter;
import com.wh.wisdomsite.xbox.utils.json.impl.FastJsonConverter;
import com.wh.wisdomsite.xbox.utils.json.impl.XmlConverter;
import com.wh.wisdomsite.xbox.common.Constants;
import com.wh.wisdomsite.xbox.common.config.SystemParameterNames;
import com.wh.wisdomsite.xbox.common.error.MainError;
import com.wh.wisdomsite.xbox.common.error.SubErrors;
import com.wh.wisdomsite.xbox.common.reponse.ErrorResponse;
import com.wh.wisdomsite.xbox.common.reponse.ServiceUnavailableErrorResponse;
import com.wh.wisdomsite.xbox.common.annotation.ExecutionType;
import com.wh.wisdomsite.xbox.core.security.InvokeTimesController;
import com.wh.wisdomsite.xbox.core.security.SecurityManager;
import com.wh.wisdomsite.xbox.core.interceptor.Interceptor;
import com.wh.wisdomsite.xbox.core.threadpool.JobDispathcher;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.StringUtils;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.RejectedExecutionException;

/**
 * 核心转发器
 * @Package com.wh.wisdomsite.xbox.core.impl
 * @author 谢泽鹏
 * @date 2014年10月8日 下午4:02:14
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2014
 * @version V1.0
 */
public class ServiceDispatcherImpl implements ServiceDispatcher {

    private static final Log logger = LogFactory.getLog(ServiceDispatcherImpl.class);

    /**
     * 安全管理器
     */
    private SecurityManager securityManager;

    /**
     * 应用上下文
     */
    private ApplicationContext applicationContext;

    /**
     * 容器上下文
     */
    private XboxContext xboxContext;

    /**
     * JSON转换器
     */
    private JosnConverter fastJsonConverter;

    /**
     * XML转换器
     */
    private JosnConverter xmlConverter;

    /**
     * 请求构造器
     */
    private RequestContextBuilder requestContextBuilder;

    /**
     * 服务适配器
     */
    private ServiceMethodAdapter serviceMethodAdapter;

    /**
     * 权重线程池
     */
    private JobDispathcher jobDispathcher;

    /**
     * 调用次数/频率管理器
     */
    private InvokeTimesController invokeTimesController;

    /**
     * 会话管理器
     */
    private SessionManager sessionManager;

    /**
     * 是否启用签名
     */
    private boolean signEnable;

    /**
     * 自定义错误路径
     */
    private String extErrorBaseName = Constants.BASE_NAMES;

    /**
     * 事件分发器
     */
    private XboxEventMulticaster xboxEventMulticaster;

    /**
     * 拦截器
     */
    private List<Interceptor> interceptors = new ArrayList<Interceptor>();

    /**
     * 监听器
     */
    private List<XboxEventListener> listeners = new ArrayList<XboxEventListener>();

    /**
     * 事件是否异步
     */
    private boolean eventAsync;


    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * 执行业务逻辑
     */
    public void execute(AsyncContext asyncContext) {
        HttpServletRequest request = (HttpServletRequest) asyncContext.getRequest();
        HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
        String method = request.getParameter(SystemParameterNames.getMethod());
        Locale locale = DefaultRequestContextBuilder.getLocale(request);
        MessageFormat messageFormat = DefaultRequestContextBuilder.getResponseFormat(request);
        ExecutionType executionType = DefaultRequestContextBuilder.getExecutionType(request);
        try {
            Job job  = null;
            if(executionType == ExecutionType.SYNC){
                logger.info("####################[xbox线程池,执行SYNC同步请求]#####################");
                job = new JobWorker(method, asyncContext);

            }else{
                logger.info("####################[xbox线程池,执行ASYNC异步请求]#####################");
                job = new AsyncJobWorker(method, asyncContext);
            }
            jobDispathcher.execute(job);
        } catch (RejectedExecutionException e) {
            //线程饱和错误
            DefaultXboxRequestContext xrc = buildRequestContextWhenException(request, e);
            xrc.setXboxResponse(new RejectedServiceErrorResponse(locale));
            writeResponse(xrc,response, messageFormat);
            fireAfterDoServiceEvent(xrc);
            asyncContext.complete();
        } catch (Throwable throwable) {
            //其他未知错误
            DefaultXboxRequestContext xrc = buildRequestContextWhenException(request, throwable);
            ServiceUnavailableErrorResponse ropResponse = new ServiceUnavailableErrorResponse(method, DefaultRequestContextBuilder.getLocale(request), throwable);
            xrc.setXboxResponse(ropResponse);
            writeResponse(xrc, response, DefaultRequestContextBuilder.getResponseFormat(request));
            fireAfterDoServiceEvent(xrc);
            asyncContext.complete();
        } finally {
            //do nothing
        }
    }





    /**
     * 同步Job工作组
     */
    private class JobWorker implements Job {
        private String key;
        private AsyncContext asyncContext;

        public JobWorker(AsyncContext context) {
            this.asyncContext = context;
        }
        public JobWorker(String key, AsyncContext context) {
            this.asyncContext = context;
            //key不为null,则取第一个
            if (StringUtils.hasText(key)) {
                key = key.split("\\.")[0];
            }
            this.key = key;
        }

        @Override
        public void run() {
            DefaultXboxRequestContext xboxRequestContext = null;
            HttpServletRequest servletRequest = null;
            HttpServletResponse servletResponse = null;
            try {
                servletRequest = (HttpServletRequest) asyncContext.getRequest();
                servletResponse = (HttpServletResponse) asyncContext.getResponse();
                /**
                 * 1. 构建DefaultXboxRequestContext对象并初始化（获取服务调用：开始时间）
                 * 2. 构建并绑定系统参数
                 */
                xboxRequestContext = requestContextBuilder.buildBySysParams(xboxContext, servletRequest);
                //验证系统参数
                MainError mainError = securityManager.validateSystemParameters(xboxRequestContext);
                if (mainError != null) {
                    setErrorResponse(xboxRequestContext, mainError);
                } else {
                    //绑定业务参数
                    requestContextBuilder.bindBusinessParams(xboxRequestContext);
                    //验证安全/频率/业务参数
                    mainError = securityManager.validateOther(xboxRequestContext);
                    if (mainError != null) {
                        setErrorResponse(xboxRequestContext, mainError);
                    } else {
                        //触发前置事件
                        firePreDoServiceEvent(xboxRequestContext);
                        //拦截器拦截
                        invokeBeforeServiceOfInterceptor(xboxRequestContext);
                        //拦截器没有结果则执行正常逻辑
                        if (xboxRequestContext.getXboxResponse() == null) {
                            Object result = doServie(xboxRequestContext);
                            xboxRequestContext.setXboxResponse(result);
                            //后置拦截,拦截输出结果
                            invokeBeforceResponseOfInterceptors(xboxRequestContext);
                        }
                    }
                }
                writeResponse(xboxRequestContext, servletResponse, xboxRequestContext.getMessageFormat());
            } catch (Throwable e) {
                //异常发生在构建xboxRequestContext时
                if (xboxRequestContext == null) {
                    xboxRequestContext = buildRequestContextWhenException(servletRequest, e);
                } else {
                    xboxRequestContext.setThrowable(e);
                    if (logger.isErrorEnabled()) {
                        logger.error("执行方法错误:" + xboxRequestContext.getMethodKey(), e);
                    }
                }
                //后置拦截,拦截输出结果
                invokeBeforceResponseOfInterceptors(xboxRequestContext);
                xboxRequestContext.setXboxResponse(new ServiceUnavailableErrorResponse(xboxRequestContext.getMethod(), xboxRequestContext.getLocale(), e));
                writeResponse(xboxRequestContext, servletResponse, xboxRequestContext.getMessageFormat());
            } finally {
                invokeTimesController.caculateInvokeTimes(xboxRequestContext);
                if(null != xboxRequestContext){
                    /**
                     * 1. 服务调用完毕后（获取服务调用：结束时间）
                     */
                    xboxRequestContext.setEndTime(System.currentTimeMillis());
                    logger.info("ServiceDispatcherImpl-->>请求服务结束时间(同步)： " + System.currentTimeMillis());
                }
                //触发事件
                fireAfterDoServiceEvent(xboxRequestContext);
                asyncContext.complete();
            }
        }

        @Override
        public String getKey() {
            return this.key;
        }

        @Override
        public long getTimeout() {
            return 0;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void doTimeout() {
            //To change body of implemented methods use File | Settings | File Templates.
        }
    }


    /**
     * 异步JOB工作组
     */
    private class AsyncJobWorker implements Job{

        private String key;
        private AsyncContext asyncContext;

        public AsyncJobWorker(AsyncContext context) {
            this.asyncContext = context;
        }
        public AsyncJobWorker(String key, AsyncContext context) {
            this.asyncContext = context;
            //key不为null,则取第一个
            if (StringUtils.hasText(key)) {
                key = key.split("\\.")[0];
            }
            this.key = key;
        }

        @Override
        public void run() {
            DefaultXboxRequestContext xboxRequestContext = null;
            HttpServletRequest servletRequest = null;
            HttpServletResponse servletResponse = null;
            try {
                servletRequest = (HttpServletRequest) asyncContext.getRequest();
                servletResponse = (HttpServletResponse) asyncContext.getResponse();
                /**
                 * 1. 构建DefaultXboxRequestContext对象并初始化（获取服务调用：开始时间）
                 * 2. 构建并绑定系统参数
                 */
                xboxRequestContext = buildRequestContextWhenAsync(servletRequest);
                //xboxRequestContext.setXboxResponse(new CommonResponse(true));
                //writeResponse(xboxRequestContext, servletResponse, xboxRequestContext.getMessageFormat());
                //asyncContext.complete();

                //验证系统参数
                MainError mainError = securityManager.validateSystemParameters(xboxRequestContext);
                if (mainError != null) {
                    setErrorResponse(xboxRequestContext, mainError);
                }
                //绑定业务参数
                else {
                    requestContextBuilder.bindBusinessParams(xboxRequestContext);
                    //验证安全/频率/业务参数
                    mainError = securityManager.validateOther(xboxRequestContext);
                    if (mainError != null) {
                        setErrorResponse(xboxRequestContext, mainError);
                    } else {
                        //触发前置事件
                        firePreDoServiceEvent(xboxRequestContext);
                        //拦截器拦截
                        invokeBeforeServiceOfInterceptor(xboxRequestContext);
                        //拦截器没有结果则执行正常逻辑
                        if (xboxRequestContext.getXboxResponse() == null) {
                            Object result = doServie(xboxRequestContext);
                            xboxRequestContext.setXboxResponse(result);
                            //后置拦截,拦截输出结果
                            invokeBeforceResponseOfInterceptors(xboxRequestContext);
                        }
                    }
                }
                writeResponse(xboxRequestContext, servletResponse, xboxRequestContext.getMessageFormat());
            } catch (Throwable e) {
                if (logger.isErrorEnabled()) {
                    logger.error("执行方法错误:" + xboxRequestContext.getMethodKey(), e);
                }
                //异常发生在构建xboxRequestContext时
                if (xboxRequestContext == null) {
                    xboxRequestContext = buildRequestContextWhenException(servletRequest, e);
                } else {
                    xboxRequestContext.setThrowable(e);
                }
                //后置拦截,拦截输出结果
                invokeBeforceResponseOfInterceptors(xboxRequestContext);
                xboxRequestContext.setXboxResponse(new ServiceUnavailableErrorResponse(xboxRequestContext.getMethod(), xboxRequestContext.getLocale(), e));
            } finally {
                invokeTimesController.caculateInvokeTimes(xboxRequestContext);
                if(null != xboxRequestContext){
                    xboxRequestContext.setEndTime(System.currentTimeMillis());
                    logger.info("ServiceDispatcherImpl->>请求服务结束时间(异步)： " + System.currentTimeMillis());
                }
                //触发事件
                fireAfterDoServiceEvent(xboxRequestContext);
                asyncContext.complete();
            }
        }

        @Override
        public String getKey() {
            return this.key;
        }

        //To change body of implemented methods use File | Settings | File Templates.
        @Override
        public long getTimeout() {
            return 0;
        }

        //To change body of implemented methods use File | Settings | File Templates.
        @Override
        public void doTimeout() {

        }
    }







    /**
     * 设置错误相应信息
     * @param xboxRequestContext
     * @param mainError
     */
    private void setErrorResponse(DefaultXboxRequestContext xboxRequestContext, MainError mainError) {
        xboxRequestContext.setMainError(mainError);
        xboxRequestContext.setXboxResponse(new ErrorResponse(mainError));
    }


    private Object doServie(DefaultXboxRequestContext xboxRequestContext) {
        return this.serviceMethodAdapter.invokeServiceMethod(xboxRequestContext);
    }


    /**
     * 响应报文
     * @param xrc
     * @param response
     * @param format
     */
    private void writeResponse(DefaultXboxRequestContext xrc, HttpServletResponse response, MessageFormat format) {
        try {
            Object result = xrc.getXboxResponse();
            String resultStr = null;
            response.setHeader("content-type", "text/html;charset=UTF-8");
            response.setCharacterEncoding(Constants.UTF8);
            Writer writer = response.getWriter();
            if (format == MessageFormat.xml) {
                //XML输出
                response.setContentType(Constants.APPLICATION_XML);
                resultStr = xmlConverter.getString(result);
            } else {
                //JSON输出
                response.setContentType(Constants.APPLICATION_JSON);
                resultStr = fastJsonConverter.getString(result);
            }
            xrc.setOutPut(resultStr);
            writer.append(resultStr);
            writer.flush();
        } catch (IOException e) {
            logger.error("writeResponse-->>响应报文IOException " + e.getMessage());
            throw new XboxException(e);
        }
    }

    @Override
    public void startup() {
        if (logger.isInfoEnabled()) {
            logger.info("开始启动Xbox框架...");
        }
        //创建对象<->字符串转化器
        this.xmlConverter = new XmlConverter();
        this.fastJsonConverter = new FastJsonConverter();
        //创建请求上下文构造器
        this.requestContextBuilder = new DefaultRequestContextBuilder(fastJsonConverter, xmlConverter);
        //创建方法适配器
        this.serviceMethodAdapter = new ServiceMethodAdapter();
        //创建Xbox上下文
        this.xboxContext = bulidXboxContext();
        //国际化消息
        initMessageSource();
        //创建事件分发器
        xboxEventMulticaster = buildXboxEventMulticaster();
        //触发容器启动事件
        fireAfterStartedXboxEvent();
        logger.info("Xbox框架启动成功...");
        logger.info("Xbox线程池状态:" + jobDispathcher.getCurrentThreadStatus());

    }

    private XboxEventMulticaster buildXboxEventMulticaster() {
        SimpleXboxEventMulticaster simpleRopEventMulticaster = new SimpleXboxEventMulticaster(eventAsync);
        //设置异步执行器
//        if (this.threadPoolExecutor != null) {
//            simpleRopEventMulticaster.setExecutor(this.threadPoolExecutor);
//        }

        //添加事件监听器
        if (this.listeners != null && this.listeners.size() > 0) {
            for (XboxEventListener ropEventListener : this.listeners) {
                simpleRopEventMulticaster.addListener(ropEventListener);
            }
        }
        return simpleRopEventMulticaster;
    }

    /**
     * 启动后事件
     */
    private void fireAfterStartedXboxEvent() {
        AfterStartedXboxEvent afterStartedXboxEvent = new AfterStartedXboxEvent(this, this.xboxContext);
        this.xboxEventMulticaster.multicaster(afterStartedXboxEvent);
    }

    /**
     * 容器关闭前事件
     */
    private void fireBeforeCloseXboxEvent() {
        PreCloseXboxEvent preCloseXboxEvent = new PreCloseXboxEvent(this, this.xboxContext);
        this.xboxEventMulticaster.multicaster(preCloseXboxEvent);
    }

    /**
     * 方法完成之后事件
     * @param xboxRequestContext
     */
    private void fireAfterDoServiceEvent(DefaultXboxRequestContext xboxRequestContext) {
        this.xboxEventMulticaster.multicaster(new AfterDoServiceEvent(this, xboxRequestContext));
    }

    /**
     * 方法调用之前
     * @param xboxRequestContext
     */
    private void firePreDoServiceEvent(DefaultXboxRequestContext xboxRequestContext) {
        this.xboxEventMulticaster.multicaster(new PreDoServiceEvent(this, xboxRequestContext));
    }

    @Override
    public JobDispathcher getJobDispathcher() {
        return jobDispathcher;
    }

    @Override
    public void setJobDispathcher(JobDispathcher jobDispathcher) {
        this.jobDispathcher = jobDispathcher;
    }



    /**
     * 构建XboxContext
     * @return
     */
    private XboxContext bulidXboxContext() {
        XboxContext xboxContext1 = new DefaultXboxContext(applicationContext);
        xboxContext1.setSessionManager(sessionManager);
        xboxContext1.setSignEnable(signEnable);
        return xboxContext1;
    }

    /**
     * 系统异常时构建{@link DefaultXboxRequestContext}
     * @param request
     * @param throwable
     * @return
     */
    private DefaultXboxRequestContext buildRequestContextWhenException(HttpServletRequest request, Throwable throwable) {
        DefaultXboxRequestContext xrc = requestContextBuilder.buildBySysParams(xboxContext, request);
        xrc.setThrowable(throwable);
        xrc.setEndTime(System.currentTimeMillis());
        logger.error("ServiceDispatcherImpl->>请求服务结束时间(Exception)： " + System.currentTimeMillis());
        return xrc;
    }


    /**
     * 异步请求时构建{@link DefaultXboxRequestContext}
     * @param request
     * @return
     */
    private DefaultXboxRequestContext buildRequestContextWhenAsync(HttpServletRequest request) {
        DefaultXboxRequestContext xrc = requestContextBuilder.buildBySysParams(xboxContext, request);
        xrc.setEndTime(System.currentTimeMillis());
        return xrc;
    }


    /**
     * 在服务之前调用拦截器
     * @param xrc
     */
    private void invokeBeforeServiceOfInterceptor(DefaultXboxRequestContext xrc) {
        Interceptor tempInterceptor = null;
        try {
            if (interceptors != null && interceptors.size() > 0) {
                for (Interceptor interceptor : interceptors) {
                    tempInterceptor = interceptor;
                    //如果匹配则开始拦截
                    if (interceptor.isMatch(xrc)) {
                        tempInterceptor.beforeService(xrc);
                        //如果某一个拦截器产生了响应,则后续拦截器不执行
                        if (xrc.getXboxResponse() != null) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("拦截器[" + interceptor.getClass().getName() + "]产生了一个xboxResponse,阻止本次服务请求继续，服务将直接返回。");
                            }
                            return;
                        }
                    }
                }
            }
        } catch (Throwable e) {
            xrc.setXboxResponse(new ServiceUnavailableErrorResponse(xrc.getMethod(), xrc.getLocale(), e));
            if (logger.isErrorEnabled()) {
                logger.error("在执行拦截器[" + tempInterceptor.getClass().getName() + "]时发生异常.", e);
            }
        }
    }


    /**
     * 在服务调用之后，返回响应之前拦截
     * @param xrc
     */
    private void invokeBeforceResponseOfInterceptors(DefaultXboxRequestContext xrc) {
        Interceptor tempInterceptor = null;
        try {
            if (interceptors != null && interceptors.size() > 0) {
                for (Interceptor interceptor : interceptors) {
                    tempInterceptor = interceptor;
                    if (interceptor.isMatch(xrc)) {
                        interceptor.beforeResponse(xrc);
                    }
                }
            }
        } catch (Throwable e) {
            xrc.setXboxResponse(new ServiceUnavailableErrorResponse(xrc.getMethod(), xrc.getLocale(), e));
            logger.error("在执行拦截器[" + tempInterceptor.getClass().getName() + "]时发生异常.", e);
        }
    }

    @Override
    public void shutdown() {
        //触发关闭事件
        fireBeforeCloseXboxEvent();
        //关闭线程池
        jobDispathcher.stopDispatcher();
        logger.info("xbox框架关闭成功...");
    }

    private void initMessageSource() {
        ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
        resourceBundleMessageSource.setBasenames(Constants.BASE_NAMES, extErrorBaseName);
        MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(resourceBundleMessageSource);
        MainErrors.setMessageSourceAccessor(messageSourceAccessor);
        SubErrors.setMessageSourceAccessor(messageSourceAccessor);
    }


    public SessionManager getSessionManager() {
        return sessionManager;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    @Override
    public SecurityManager getSecurityManager() {
        return securityManager;
    }

    public void setSecurityManager(SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

    @Override
    public void addInterceptor(Interceptor interceptor) {
        this.interceptors.add(interceptor);
    }

    @Override
    public void addListener(XboxEventListener listener) {
        this.listeners.add(listener);
    }

    @Override
    public XboxContext getXboxContext() {
        return xboxContext;
    }

    @Override
    public void setXboxContext(XboxContext xboxContext) {
        this.xboxContext = xboxContext;
    }

    public boolean isSignEnable() {
        return signEnable;
    }

    @Override
    public void setSignEnable(boolean signEnable) {
        this.signEnable = signEnable;
    }

    public String getExtErrorBaseName() {
        return extErrorBaseName;
    }

    @Override
    public void setExtErrorBaseName(String extErrorBaseName) {
        this.extErrorBaseName = extErrorBaseName;
    }

    public InvokeTimesController getInvokeTimesController() {
        return invokeTimesController;
    }

    @Override
    public void setInvokeTimesController(InvokeTimesController invokeTimesController) {
        this.invokeTimesController = invokeTimesController;
    }

    public boolean isEventAsync() {
        return eventAsync;
    }

    @Override
    public void setEventAsync(boolean eventAsync) {
        this.eventAsync = eventAsync;
    }
}
